package com.zlict.commonelasticsearch.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zlict.commonelasticsearch.domain.AuditLog;
import com.zlict.commonelasticsearch.domain.AuditReportQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Elasticsearch审计日志服务 - 现代 Java API（ES 8.13.4 版本）
 * 使用类型安全的 Elasticsearch 8.13.4 Java API
 * 
 * @author zlict
 */
@Service
@Slf4j
public class ElasticsearchAuditService {
    
    @Autowired
    private ElasticsearchClient client;
    
    /**
     * 保存审计日志到Elasticsearch - 使用现代 Java API
     */
    public boolean saveAuditLog(String userId, String operation, String result, String details) {
        try {
            // 使用按日期命名的索引格式
            String indexName = "audit-logs-" + new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            
            // 构建审计日志数据
            Map<String, Object> auditLog = buildAuditLogData(userId, operation, result, details);
            
            // 使用类型安全的 IndexRequest
            IndexRequest<Map<String, Object>> request = IndexRequest.of(i -> i
                .index(indexName)
                .document(auditLog)
            );
            
            // 执行请求
            IndexResponse response = client.index(request);
            
            log.debug("审计日志已保存: userId={}, operation={}, result={}", userId, operation, result);
            return response.result().name().equals("Created") || response.result().name().equals("Updated");
            
        } catch (Exception e) {
            log.error("保存审计日志失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 保存审计日志到Elasticsearch（使用用户名）
     */
    public boolean saveAuditLogByUsername(String username, String operation, String result, String details) {
        // 对于只有用户名的情况，直接使用用户名作为userId
        return saveAuditLog(username, operation, result, details);
    }
    
    /**
     * 构建审计日志数据
     */
    private Map<String, Object> buildAuditLogData(String userId, String operation, String result, String details) {
        Map<String, Object> auditLog = new HashMap<>();
        auditLog.put("timestamp", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(new Date()));
        auditLog.put("userId", userId);
        auditLog.put("operation", operation);
        auditLog.put("result", result);
        auditLog.put("details", details);
        return auditLog;
    }
    
    /**
     * 检查Elasticsearch连接状态 - 使用现代 Java API
     */
    public boolean isElasticsearchAvailable() {
        try {
            var response = client.ping();
            return response.value();
        } catch (Exception e) {
            log.error("检查Elasticsearch连接失败: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 获取集群信息 - 使用现代 Java API
     */
    public String getClusterInfo() {
        try {
            var response = client.info();
            
            StringBuilder info = new StringBuilder();
            info.append("Cluster Name: ").append(response.clusterName()).append("\n");
            info.append("Version: ").append(response.version().number()).append("\n");
            info.append("Lucene Version: ").append(response.version().luceneVersion()).append("\n");
            
            return info.toString();
        } catch (Exception e) {
            log.error("获取集群信息失败: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 查询审计日志 - 使用现代 Java API
     */
    public String queryAuditLogs() {
        try {
            // 使用通配符匹配所有按日期命名的索引
            String indexPattern = "audit-logs-*";
            
            // 创建查询请求
            SearchRequest request = SearchRequest.of(s -> s
                .index(indexPattern)
                .size(10)
                .sort(sort -> sort.field(f -> f.field("timestamp").order(SortOrder.Desc)))
            );
            
            // 执行请求
            SearchResponse<ObjectNode> response = client.search(request, ObjectNode.class);
            
            // 转换为JSON字符串
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(response);
            
        } catch (Exception e) {
            log.error("查询审计日志失败: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 查询最近几天的审计日志 - 使用现代 Java API
     */
    public String queryRecentAuditLogs(int days) {
        try {
            // 使用通配符匹配所有按日期命名的索引
            String indexPattern = "audit-logs-*";
            log.info("开始查询审计日志，索引模式: {}", indexPattern);
            
            // 查询所有审计日志索引
            SearchRequest request = SearchRequest.of(s -> s
                .index(indexPattern)  // 使用通配符匹配审计日志索引
                .size(50)
                .sort(sort -> sort.field(f -> f.field("timestamp").order(SortOrder.Desc)))
            );
            
            log.info("执行ES查询请求");
            
            // 执行请求
            SearchResponse<ObjectNode> response = client.search(request, ObjectNode.class);
            
            log.info("ES查询完成，命中数量: {}", response.hits().total().value());
            log.info("查询的索引: {}", request.index());
            log.info("响应状态: {}", response.shards().failed());
            
            // 检查是否有数据
            if (response.hits().total().value() == 0) {
                log.warn("没有找到任何审计日志数据");
                return "{\"hits\":{\"total\":{\"value\":0},\"hits\":[]}}";
            }
            
            // 提取hits数据并转换为JSON字符串，与queryAllAuditLogs保持一致
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode resultNode = mapper.createObjectNode();
            
            // 添加hits数据
            ArrayNode hitsArray = mapper.createArrayNode();
            for (Hit<ObjectNode> hit : response.hits().hits()) {
                hitsArray.add(hit.source());
            }
            resultNode.set("hits", hitsArray);
            
            // 添加总数信息
            resultNode.put("total", response.hits().total().value());
            
            String result = mapper.writeValueAsString(resultNode);
            
            log.info("查询结果长度: {}", result.length());
            log.info("查询结果内容: {}", result);
            return result;
            
        } catch (Exception e) {
            log.error("查询最近审计日志失败: {}", e.getMessage(), e);
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }
    
    /**
     * 直接查询所有审计日志 - 用于调试
     */
    public String queryAllAuditLogs() {
        try {
            // 直接查询所有索引
            SearchRequest request = SearchRequest.of(s -> s
                .index("_all")
                .size(100)
                .sort(sort -> sort.field(f -> f.field("timestamp").order(SortOrder.Desc)))
            );
            
            log.info("执行直接查询所有索引");
            
            // 执行请求
            SearchResponse<ObjectNode> response = client.search(request, ObjectNode.class);
            
            log.info("直接查询完成，命中数量: {}", response.hits().total().value());
            
            // 提取hits数据并转换为JSON字符串
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode resultNode = mapper.createObjectNode();
            
            // 添加hits数据
            ArrayNode hitsArray = mapper.createArrayNode();
            for (Hit<ObjectNode> hit : response.hits().hits()) {
                hitsArray.add(hit.source());
            }
            resultNode.set("hits", hitsArray);
            
            // 添加总数信息
            resultNode.put("total", response.hits().total().value());
            
            String result = mapper.writeValueAsString(resultNode);
            
            log.info("直接查询结果长度: {}", result.length());
            log.info("直接查询结果内容: {}", result);
            return result;
            
        } catch (Exception e) {
            log.error("直接查询失败: {}", e.getMessage(), e);
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }
    
    /**
     * 查询审计日志用于报告生成 - 使用现代 Java API
     * 根据查询条件从ES获取审计日志数据
     */
    public List<AuditLog> queryAuditLogsForReport(AuditReportQuery query) {
        try {
            // 使用通配符匹配所有按日期命名的索引
            String indexPattern = "audit-logs-*";
            
            // 构建复杂的bool查询
            List<Query> mustQueries = new ArrayList<>();
            
            // 时间范围查询
            if (query.getStartTime() != null || query.getEndTime() != null) {
                mustQueries.add(Query.of(q -> q.range(r -> {
                    RangeQuery.Builder rangeBuilder = r.field("timestamp");
                    if (query.getStartTime() != null) {
                        rangeBuilder.gte(JsonData.of(
                            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(query.getStartTime())
                        ));
                    }
                    if (query.getEndTime() != null) {
                        rangeBuilder.lte(JsonData.of(
                            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(query.getEndTime())
                        ));
                    }
                    return rangeBuilder;
                })));
            }
            
            // 用户名查询（使用通配符）
            if (query.getUsername() != null && !query.getUsername().isEmpty()) {
                mustQueries.add(Query.of(q -> q.wildcard(w -> w
                    .field("userId")
                    .value("*" + query.getUsername() + "*")
                )));
            }
            
            // 操作类型查询
            if (query.getActionType() != null && !query.getActionType().isEmpty()) {
                mustQueries.add(Query.of(q -> q.term(t -> t
                    .field("operation")
                    .value(FieldValue.of(query.getActionType()))
                )));
            }
            
            // 操作结果查询
            if (query.getActionResult() != null && !query.getActionResult().isEmpty()) {
                mustQueries.add(Query.of(q -> q.term(t -> t
                    .field("result")
                    .value(FieldValue.of(query.getActionResult()))
                )));
            }
            
            // 构建查询请求
            SearchRequest.Builder searchBuilder = new SearchRequest.Builder()
                .index(indexPattern)
                .size(1000)
                .sort(sort -> sort.field(f -> f.field("timestamp").order(SortOrder.Desc)));
            
            // 如果有查询条件，则添加bool查询
            if (!mustQueries.isEmpty()) {
                searchBuilder.query(q -> q.bool(b -> b.must(mustQueries)));
            }
            
            SearchRequest searchRequest = searchBuilder.build();
            
            // 执行请求
            SearchResponse<ObjectNode> response = client.search(searchRequest, ObjectNode.class);
            
            // 解析结果
            return parseAuditLogsFromSearchResponse(response);
            
        } catch (Exception e) {
            log.error("查询报告审计日志失败: {}", e.getMessage(), e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 解析SearchResponse为AuditLog对象列表 - 使用现代 Java API
     */
    private List<AuditLog> parseAuditLogsFromSearchResponse(SearchResponse<ObjectNode> response) {
        List<AuditLog> auditLogs = new ArrayList<>();
        
        try {
            var hits = response.hits().hits();
            
            for (var hit : hits) {
                ObjectNode sourceNode = hit.source();
                if (sourceNode != null) {
                    AuditLog auditLog = parseAuditLogFromJsonNode(sourceNode);
                    if (auditLog != null) {
                        auditLogs.add(auditLog);
                    }
                }
            }
            
        } catch (Exception e) {
            log.error("解析审计日志响应失败: {}", e.getMessage(), e);
        }
        
        return auditLogs;
    }
    
    /**
     * 从ES源数据解析AuditLog对象 - 使用现代 Java API
     */
    private AuditLog parseAuditLogFromJsonNode(JsonNode sourceNode) {
        try {
            AuditLog auditLog = new AuditLog();
            
            // 解析时间戳
            String timestamp = sourceNode.path("timestamp").asText();
            if (!timestamp.isEmpty()) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                auditLog.setActionTime(sdf.parse(timestamp));
            }
            
            // 解析用户ID
            String userId = sourceNode.path("userId").asText();
            auditLog.setUsername(userId);
            
            // 解析操作类型
            String operation = sourceNode.path("operation").asText();
            auditLog.setActionType(operation);
            
            // 解析操作结果
            String result = sourceNode.path("result").asText();
            auditLog.setActionResult(result);
            
            // 解析详细信息
            String details = sourceNode.path("details").asText();
            auditLog.setDetails(details);
            
            // 从详细信息中提取IP地址（如果存在）
            if (details.contains("IP地址: ")) {
                String[] parts = details.split("IP地址: ");
                if (parts.length > 1) {
                    String ipPart = parts[1].split(",")[0].trim();
                    auditLog.setIpAddress(ipPart);
                }
            }
            
            return auditLog;
            
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
